package com.clei.utils.helper;

import com.clei.utils.FileUtil;
import com.clei.utils.PrintUtil;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 * 文本文件分割器
 * mysql导个表sql，2.4G，sqlyog打不开，notepad也打不开
 * 切割一下
 *
 * @author Y
 * @date 2022-04-28
 */
public class FileSplitter {

    /**
     * 切割文件行数限制
     */
    private final int lineLimit;
    /**
     * 分隔文件路径
     */
    private final String inputFilePath;
    /**
     * 输出文件目录路径
     */
    private final String outputDirectoryPath;
    /**
     * 行过滤器
     */
    Predicate<String> lineFilter;
    /**
     * 首行处理器
     */
    Function<String, String> headHandler;
    /**
     * 尾行处理器
     */
    Function<String, String> tailHandler;
    /**
     * 行号
     */
    private int lineNumber;
    /**
     * FileWriter
     */
    private FileWriter fw;

    /**
     * BufferedWriter
     */
    private BufferedWriter bw;

    /**
     * 文件名 不包含后缀
     */
    private String fileName;

    /**
     * 文件后缀名
     */
    private String fileSuffix;

    /**
     * 临时行
     */
    private String tempLine;

    public FileSplitter(String inputFilePath, String outputDirectoryPath, int lineLimit, Predicate<String> lineFilter,
                        Function<String, String> headHandler, Function<String, String> tailHandler) {
        this.inputFilePath = inputFilePath;
        this.outputDirectoryPath = outputDirectoryPath;
        this.lineLimit = lineLimit;
        this.lineFilter = lineFilter;
        this.headHandler = headHandler;
        this.tailHandler = tailHandler;
    }

    public static void main(String[] args) throws Exception {
        FileSplitter splitter = new FileSplitter("E:\\tmp\\test_data.sql",
                "E:\\tmp\\test_data", 800000,
                s -> s.startsWith("("),
                s -> "INSERT INTO test_data(id,column1,column2) VALUES \n" + s,
                s -> s.substring(0, s.length() - 1) + ";");
        splitter.split();
    }

    /**
     * 开始分割
     */
    public void split() {
        // 设置名称数据
        File inputFile = new File(this.inputFilePath);
        String inputFileName = inputFile.getName();
        int dotIndex = inputFileName.lastIndexOf('.');
        if (-1 != dotIndex) {
            this.fileName = inputFileName.substring(0, dotIndex);
            this.fileSuffix = inputFileName.substring(dotIndex);
        } else {
            this.fileName = inputFileName;
            this.fileSuffix = "";
        }
        // 行处理
        FileUtil.txtHandle(inputFile, (lineNumber, line) -> {
            if (null != this.lineFilter) {
                if (lineFilter.test(line)) {
                    this.add(line);
                }
            } else {
                this.add(line);
            }
        });
        this.writeTail();
    }

    /**
     * 添加行
     *
     * @param line 行数据
     */
    private void add(String line) {
        try {
            if (this.lineNumber % this.lineLimit == 0) {
                // 尾行处理
                if (null != this.tailHandler && null != this.tempLine) {
                    this.tempLine = this.tailHandler.apply(this.tempLine);
                }
                write(this.tempLine);
                // 清理
                updateAndClean();
                // 首行处理
                if (null != this.headHandler && null != line) {
                    line = this.headHandler.apply(line);
                }
            } else {
                write(this.tempLine);
            }
            this.tempLine = line;
            this.lineNumber++;
        } catch (Exception e) {
            PrintUtil.log("添加行出错 line : {}", line, e);
        }
    }

    /**
     * 写一行
     *
     * @param line String
     * @throws Exception 各种异常
     */
    private void write(String line) throws Exception {
        if (null != line) {
            this.bw.write(line);
            this.bw.newLine();
        }
    }

    /**
     * 写入最后一行
     */
    private void writeTail() {
        try {
            if (null != this.tailHandler && null != this.tempLine) {
                this.tempLine = this.tailHandler.apply(this.tempLine);
                this.bw.write(this.tempLine);
            }
            if (null != this.bw) {
                this.bw.flush();
                this.bw.close();
                this.fw.close();
            }
        } catch (Exception e) {
            PrintUtil.log("writeTail出错 line : {}", this.tempLine, e);
        }
    }

    /**
     * 清理并更新 bw
     *
     * @throws Exception 各种异常
     */
    private void updateAndClean() throws Exception {
        if (null != this.bw) {
            this.bw.flush();
            this.bw.close();
            this.fw.close();
        }
        String outputFilePath = this.outputDirectoryPath + File.separator + this.fileName
                + (this.lineNumber / this.lineLimit) + fileSuffix;
        PrintUtil.log(outputFilePath);
        this.fw = new FileWriter(outputFilePath);
        this.bw = new BufferedWriter(this.fw);
    }
}
